using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Reflection;
using HtmlAgilityPack;
using NUnit.Framework;
using Vk.Methods.Common;

namespace UnitTests
{
    public abstract class TestsBase
    {
        protected void IsSignatureUpdated(MethodInfo methodBase, string urlToCheck, MethodBase callerMethod = null)
        {
            var parameters = methodBase.GetParameters().Select(x => x.Name);
            var onlineParams = GetOnlineParams(urlToCheck);

            //Check if calling test has a special attribute indicating that VK API method has an extended version
            MethodBase caller = callerMethod;
            if (caller == null)
            {
                StackFrame frame = new StackFrame(1);
                caller = frame.GetMethod();
            }

            var attributes =
                caller.GetCustomAttributes().OfType<IParametersChain>();
            foreach (var attribute in attributes)
            {
                attribute.ProcessParameters(onlineParams);
            }

            //Compute difference
            var exc = onlineParams.Except(parameters, StringComparer.InvariantCultureIgnoreCase);
            if (exc.Any())
            {
                Assert.Fail("There are not equal parameters: " + string.Join(",", exc));
            }
        }

        /// <summary>
        /// Figures out a URL of VK documentation using name of calling test method and checks the VK API method's signature
        /// </summary>
        /// <param name="methodBase">Method to be tested</param>
        protected void IsSignatureUpdated(MethodInfo methodBase)
        {
            var frame = new StackFrame(1);
            var method = frame.GetMethod();

            IsSignatureUpdated(methodBase,
                               "https://vk.com/dev/" +
                               method.DeclaringType.Name.Replace("Tests", string.Empty).ToLowerInvariant() + "." +
                               method.Name.Replace("Test", string.Empty).WithFirstSmallLetter(),
                               method);
        }

        /// <summary>
        /// Download online documentation and parse parameters from it
        /// </summary>
        /// <param name="urlToCheck">URL of the page with the documentation</param>
        /// <returns>List of parameters already casted from snake_case to camelCase</returns>
        private List<string> GetOnlineParams(string urlToCheck)
        {
            var client = new WebClient();
            var html = client.DownloadString(urlToCheck);
            var doc = new HtmlDocument();
            doc.LoadHtml(html);
            var onlineParamsNodes =
                doc.DocumentNode.SelectNodes("//table[@class='dev_params_table']/tr/td[@class='dev_param_name']");
            return onlineParamsNodes?.Select(x => x.InnerText.ToCamelCase())
                                     .ToList() ?? new List<string>(0);
        }
    }
}